package com.azzcs.mvc.servlet;

import com.azzcs.mvc.mav.ModelAndView;
import com.azzcs.mvc.mav.View;
import com.azzcs.mvc.mav.ViewResolver;
import com.azzcs.mvc.rm.RequestMappingHandlerMapping;
import com.azzcs.spring.ApplicationContent;
import com.azzcs.spring.anno.RequestMapping;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @Author: wzg
 * @Date: 2020/12/25 下午5:58
 */
public class DispatcherServlet extends HttpServlet {
    ApplicationContent applicationContent;
    List<HandlerMapping> handlerMappings = new ArrayList<>();
    List<HandlerAdapter> handlerAdapters = new ArrayList<>();
    Map<String,HandlerMethod> handlerMethodMap = new HashMap<>();
    List<HandlerInterceptor> handlerInterceptors = new ArrayList<>();
    List<ViewResolver> viewResolvers = new ArrayList<>();
    private Properties properties = new Properties();

    private final static String SCAN_PACKAGE = "scanPackage";
    private final static String MVC_PACKAGE = "com.azzcs.mvc";


    @Override
    public void init() {
        initProperties();
        initApplicationContent();
        initHandlerMethod();
        initHandlerMapping();
        initHandlerAdapter();
        initHandlerInterceptor();
        initViewResolver();
    }

    private void initViewResolver() {
        List<String> beanNames = applicationContent.getBeanNames();
        for (String beanName : beanNames) {
            Object bean = applicationContent.getBean(beanName);
            if(bean instanceof ViewResolver){
                viewResolvers.add((ViewResolver)bean);
            }
        }
    }

    private void initHandlerInterceptor() {
        List<String> beanNames = applicationContent.getBeanNames();
        for (String beanName : beanNames) {
            Object bean = applicationContent.getBean(beanName);
            if(bean instanceof HandlerInterceptor){
                handlerInterceptors.add((HandlerInterceptor)bean);
            }
        }
    }

    private void initHandlerMethod() {
        List<String> beanNames = applicationContent.getBeanNames();
        for (String beanName : beanNames) {
            Object bean = applicationContent.getBean(beanName);
            Class<?> clazz = bean.getClass();
            RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
            if(requestMapping != null){
                String pathPrefix = requestMapping.value();
                Method[] declaredMethods = clazz.getDeclaredMethods();
                for (Method method : declaredMethods) {
                    RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
                    if(methodRequestMapping != null){
                        String pathSuffix = methodRequestMapping.value();
                        String path = pathPrefix + pathSuffix;
                        HandlerMethod handlerMethod = new HandlerMethod(bean,method);
                        handlerMethodMap.put(path,handlerMethod);
                    }
                }
            }
        }
    }

    private void initHandlerAdapter() {
        List<String> beanNames = applicationContent.getBeanNames();
        for (String beanName : beanNames) {
            Object bean = applicationContent.getBean(beanName);
            if(bean instanceof HandlerAdapter){
                handlerAdapters.add((HandlerAdapter)bean);
            }
        }
    }

    private void initHandlerMapping() {
        List<String> beanNames = applicationContent.getBeanNames();
        for (String beanName : beanNames) {
            Object bean = applicationContent.getBean(beanName);
            if(bean instanceof HandlerMapping){
                HandlerMapping handlerMapping = (HandlerMapping) bean;
                if(handlerMapping instanceof RequestMappingHandlerMapping){
                    ((RequestMappingHandlerMapping) handlerMapping).setHandlerMethodMap(this.handlerMethodMap);
                    ((RequestMappingHandlerMapping) handlerMapping).setHandlerInterceptors(this.handlerInterceptors);
                }
                handlerMappings.add(handlerMapping);
            }
        }
    }

    private void initApplicationContent() {
        String basePage = properties.getProperty(SCAN_PACKAGE);
        applicationContent = new ApplicationContent(basePage,MVC_PACKAGE);
    }


    private void initProperties() {
        ServletConfig servletConfig = this.getServletConfig();
        String location = servletConfig.getInitParameter("contextConfigLocation");
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(location);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doDispatch(req,resp);
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
        // 获取执行器链
        HandlerExecutionChain executionChain = getHandlerMapping(req);
        // 获取处理器适配器
        HandlerAdapter adapter = getHandlerAdapter(executionChain.getHandler());
        // 执行拦截器前置方法
        if(!executionChain.preHandle(req,resp,executionChain.getHandler())){
            return;
        }
        // 执行业务逻辑
        ModelAndView mav = adapter.handler(req, resp, executionChain.getHandler());
        // 执行拦截器后置方法
        executionChain.postHandle(req,resp,executionChain.getHandler());
        // 处理结果
        processDispatchResult(req,resp,executionChain,mav);
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, HandlerExecutionChain executionChain, ModelAndView mav) {
        for (ViewResolver viewResolver : viewResolvers) {
            View view = viewResolver.resolveViewName(mav.getView());
            if(view != null){
                view.render(mav.getModel(),req,resp);
            }
        }

    }

    private HandlerAdapter getHandlerAdapter(HandlerMethod handler) {
        for (HandlerAdapter handlerAdapter : handlerAdapters) {
            if(handlerAdapter.support(handler)){
                return handlerAdapter;
            }
        }
        throw new RuntimeException("找不到处理器适配器");
    }

    private HandlerExecutionChain getHandlerMapping(HttpServletRequest req) {
        for (HandlerMapping handlerMapping : handlerMappings) {
            HandlerExecutionChain handle = handlerMapping.handle(req);
            if(handle != null){
                return handle;
            }
        }
        throw new RuntimeException("找不到处理器映射器");
    }
}
